function Torrent(datatable, link) {
    this.datatable = datatable;
    
	this.unique_id = datatable.fnSettings().fnRecordsTotal();
	this.name = link.substring(0, 20) + (link.length > 20 ? '...' : '');
	this.status = 'Initializing';
	this.added_on = new Date().toLocaleString();
	this.progressbar = '<div id="progress' + this.unique_id + '"><span></span></div>';
	this.size = 'Unknown';
	this.upload_rate = 0;
	this.download_rate = 0;
	this.location = 'location';
    
	this.link = link;
	this.progress = 0;
	
    this.index = datatable.fnAddData(this)[0];
    
    this.metadata = null;
	
	var _this = this;
    
    this.tr = datatable.fnSettings().aoData[this.index].nTr;
    $(this.tr).find('td').on('dblclick', function() {
    	_this.datatable.fnIsOpen(_this.tr) ? _this.datatable.fnClose(_this.tr) : _this.datatable.fnOpen(_this.tr, _this.fnFormatDetails(), 'details');
    });
	
    this.watch('status', this.on_status_change);
    this.watch('name', this.on_name_change);
    this.watch('size', this.on_size_change);
    
    this.dirEntry = null;
    
	window.webkitRequestFileSystem(PERSISTENT, 10240, function(fs) {
		fs.root.getDirectory(_this.unique_id, {create: true}, function(dirEntry) {
			_this.location = '<a href="' + dirEntry.toURL() + '" target="_blank">' + dirEntry.fullPath + '</a>';
			_this.dirEntry = dirEntry;
	        var aData = _this.datatable.fnGetData(_this.index);
	        aData.location = _this.location;
	        datatable.fnUpdate(aData, _this.index);
	        
			_this.play(); 
		});
	}, function(e) {
		_this.update_status('Error filesystem');
		console.log('Error', e);
	});
	
    this.update_progressbar();
}

Torrent.prototype.fnFormatDetails = function() {
	var show_file = this.link;
	var _this = this;
	this.dirEntry.getFile('torrent.txt', {create: false}, function(fileEntry) {
		chrome.fileSystem.getDisplayPath(fileEntry, function(displayPath) {
			console.log(displayPath); // Bug: http://src.chromium.org/viewvc/chrome?view=rev&revision=142672
		});
		show_file = '<a href="' + fileEntry.toURL() + '" target="_blank">' + show_file + '</a>';
		$('#details' + _this.unique_id + ' .torrent').html(show_file);
	}, function(e) {
		console.log('Error', e);
	});
    var sOut = '<div class="expanded-row" id="details' + this.unique_id + '"><ul>';
    sOut += '<li>Torrent: <span class="torrent">' + show_file + '</span></li>';
    sOut += '</ul></div>';
    return sOut;
};

Torrent.prototype.update_progressbar = function() {
	$('#progress' + this.unique_id).progressbar({value: this.progress});
	$('#progress' + this.unique_id + ' span').html(this.progress + '% - ' + this.status);
};

Torrent.prototype.pause = function() {
	if(this.status != 'Paused' && status != 'Completed') {
		this.update_status('Paused');
		// TODO: Pause webworkers
	}
};

Torrent.prototype.play = function() {
	if(this.status != 'Downloading' && status != 'Completed') {
		this.update_status('Downloading');
		if(!this.metadata && !this.magnet_info) {
			if(this.link.match(/^magnet:/)) {
				this.get_magnet_info();
			} else {
				this.download_torrent_file();
			}
		}
		// TODO: Start webworkers
	}
};

Torrent.prototype.on_status_change = function(id, oldval, newval) {
	if(oldval == 'Completed') {
    	return 'Completed';
	} else if(oldval != newval) {
        var aData = this.datatable.fnGetData(this.index);
        aData.status = this.status;
        datatable.fnUpdate(aData, this.index);
    }
    return newval;
}
Torrent.prototype.update_status = function(newstatus) {
	this.status = newstatus;
    this.update_progressbar();
};

Torrent.prototype.on_name_change = function(id, oldval, newval) {
    var aData = this.datatable.fnGetData(this.index);
    aData.name = newval;
    this.datatable.fnUpdate(aData, this.index);
    return newval;
};

Torrent.prototype.on_size_change = function(id, oldval, newval) {
    var aData = this.datatable.fnGetData(this.index);
    if($.isNumeric(newval)) {
    	aData.size = filesize(newval);
    }
    this.datatable.fnUpdate(aData, this.index);
    return newval;
};

Torrent.prototype.download_torrent_file = function() {
	var xhr = new XMLHttpRequest();
	xhr.responseType = 'arraybuffer';
	var _this = this;
	xhr.onload = function() {
		_this.dirEntry.getFile('downloaded.torrent', {create: true}, function(fileEntry) {
			fileEntry.createWriter(function(fileWriter) {
				fileWriter.onerror = function(e) {
					_this.update_status('Error on write');
					console.log('Error', e);
				};
				try {
					_this.metadata = bdecode(String.fromCharCode.apply(null, new Uint8Array(xhr.response)));
					_this.size = _this.metadata.info.length;
					_this.name = _this.metadata.info.name;
					_this.update_status('Downloading'); // Why is this necessary?
					_this.initialize_trackers();
				} catch(e) {
					_this.update_status('Error type');
					console.log('Error', e);
				} finally {
					var blob = new Blob([new DataView(xhr.response)], {type: 'application/x-bittorrent'});
					fileWriter.write(blob);
				}
			}, function(e) {
				_this.update_status('Error creating');
				console.log('Error', e);
			});
		}, function(e) {
			_this.update_status('Error directory');
			console.log('Error', e);
		});
	};
	xhr.onerror = function() {
		_this.update_status('Error downloading');
	};
	xhr.open("GET", this.link, true);
	xhr.send();
};

Torrent.prototype.get_magnet_info = function() {
	var uri = this.link.slice(this.link.indexOf(':') + 2);
	var parts = uri.split('&');
	var d = {};
	for(var i = 0; i < parts.length; i++) {
		var kv = parts[i].split('=');
		var k = decodeURIComponent(kv[0]);
        var v = decodeURIComponent(kv[1]);
        if(!d[k]) {
            d[k] = [];
        }
        d[k].push(v);
	}
    var xtparts = d.xt[0].split(':');

    var encodedhash = xtparts[xtparts.length-1];
    if(encodedhash.length == 40) {
        this.hash_hex = encodedhash.toLowerCase();
        this.hash = hex2arr(this.hash_hex);
    } else if(encodedhash.length == 32) {
        var output = b32decode(encodedhash);
        this.hash = str2arr(output);
        this.hash_hex = ab2hex(this.hash).toLowerCase();
    }
    this.magnet_info = d;
	this.name = this.magnet_info.dn;
	this.update_status('Downloading'); // Why is this necessary?
	this.initialize_trackers();
};

Torrent.prototype.initialize_trackers = function() {
    var strs = [];
    if(this.magnet_info && this.magnet_info.tr) {
        for(var i = 0; i < this.magnet_info.tr.length; i++) {
            strs.push(this.magnet_info.tr[i]);
        }
    } else if(this.metadata) {
        if(this.metadata['announce-list']) {
            for(var tn = 0; tn < this.metadata['announce-list'].length; tn++) {
                var tier = this.metadata['announce-list'][tn];
                for(var i = 0; i < this.metadata['announce-list'][tn].length; i++) {
                    strs.push(this.metadata['announce-list'][tn][i]);
                }
            }
        } else if(this.metadata['announce']) {
            if(typeof this.metadata['announce'] == 'string') {
                strs.push(this.metadata['announce']);
            } else {
            	this.update_status('Error tracker announce');
            }
        }
    } else {
    	this.update_status('Error tracker');
    }
    for(var i = 0; i < strs.length; i++) {
        if(strs[i].match(/^udp:\/\//)) {
            // TODO: UDP Tracker
        } else {
            // TODO: HTML Tracker
        }
    }
};